package com.sunday.authorization.mock.oauth2.server.authorization.redis.restrict;

import org.springframework.data.redis.core.script.RedisScript;


public interface RedisScriptLua {

    /**
     * @Description: 限制用户同时登录数量，进行 refresh token 适配, 默认实现策略为后踢前
     * ---> 返回 无
     * <p>
     * 1. KEYS[1] principalName                账号
     * 2. KEYS[2] access token                 访问token
     * 3. KEYS[3] refresh token                刷新token
     * 4. ARGV[1] login max                    同时在线数量
     * 5. ARGV[2] access expires at            访问token 过期时间/秒
     * 6. ARGV[3] principalName excess at      账号 生存刷新时间/秒
     */
    RedisScript<Void> RESTRICTION = RedisScript.of(
            """
                    local quantity = redis.call('zcard', KEYS[1])\s\
                    if tonumber(ARGV[1]) > quantity then\s\
                        redis.call('zadd',KEYS[1], ARGV[2], KEYS[2])\s\
                        redis.call('expire',KEYS[1], ARGV[3])\s\
                    else\s\
                        local currentTime = redis.call('TIME')[1]\s\
                        local cursor = 0\s\
                        local clear = false\s\
                        local first = false\s\
                        local old_access_token\s\
                        if KEYS[3] and KEYS[3] ~= '' then\s\
                            local refreshToken = redis.call('get',KEYS[3])\s\
                            if refreshToken ~= false then\s\
                                local parts = {}\s\
                                for part in string.gmatch(refreshToken, '[^#]+') do\s\
                                    table.insert(parts, part)\s\
                                end\s\
                                old_access_token = parts[2]\s\
                            end\s\
                        end\s\
                        repeat\s\
                            local members = redis.call('zscan', KEYS[1], cursor)\s\
                            cursor = tonumber(members[1])\s\
                            for i = 1, #members[2], 2 do\s\
                                local member = members[2][i]\s\
                                local score = members[2][i + 1]\s\
                                if first == false then\s\
                                    first = member\s\
                                end\s\
                                if (tonumber(score) < tonumber(currentTime)) or (old_access_token == member) then\s\
                                    clear = redis.call('zrem', KEYS[1], member)\s\
                                end\s\
                            end\s\
                        until cursor == 0\s\
                        if clear ~= false then\s\
                            redis.call('zadd',KEYS[1], ARGV[2], KEYS[2])\s\
                            redis.call('expire',KEYS[1], ARGV[3])\s\
                        else\s\
                            redis.call('zrem',KEYS[1], first)\s\
                            redis.call('zadd',KEYS[1], ARGV[2], KEYS[2])\s\
                            redis.call('expire',KEYS[1], ARGV[3])\s\
                        end\s\
                    end 
                    """
    );

}
